﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization;

namespace MonsterNet
{
	public enum MNFSObjectLocation
	{
		Source = 0,
		Destination = 1,
        Portable = 2,
		NumberOfLocations = 3
	}

	public class MNChildUpdateEventArgs : EventArgs {
		
		private MNFSObjectLocation _location;
		private bool _newFind;				
		private DateTime _found;
		
		public MNFSObjectLocation Location {
			get { return _location; }
			set { _location = value; }
		}

		public bool NewFind {
			get { return _newFind; }
			set { _newFind = value; }
		}
		public DateTime Found {
			get { return _found; }
			set { _found = value; }
		}		
			
		public MNChildUpdateEventArgs(MNFSObjectLocation location, DateTime newChildFound) {
			_location = location;
			_newFind = true;
			_found = newChildFound;	
		}		
	}
	
	[Serializable]
	public abstract class MNFSObject: ISerializable {
		
		private const string CrossPlatformDirectorySeperatorPlaceHolder = @"[/]"; //substituded in at serialisation and out during deserialisation to maintain cross platform combatibility
		protected string _name;
		protected string _rootlessPath;
		

		/// <summary>
		/// Case Insensitive filename of the File System Object
		/// </summary>
		public string Name {   
			get { return _name; } 
		}		
		
		public string RootlessPath {
			get { return _rootlessPath;	}
		}
		
		public string RootlessPathname {
			get { return System.IO.Path.Combine(RootlessPath, Name); }
		}
		
		/// <summary>
		/// Case sensitive filename of the File System Object 
		/// </summary>
		/// <param name="location">
		/// A <see cref="MNFSObjectLocation"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.String"/>
		/// </returns>
		/*public string GetNameForLocation(MNFSObjectLocation location) {
			return _names[(int)location];
		}
		
		/// <summary>
		/// Case sensitive Path of the File System Object 
		/// </summary>
		/// <param name="location">
		/// A <see cref="MNFSObjectLocation"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.String"/>
		/// </returns>
		public string GetRootlessPathForLocation(MNFSObjectLocation location) {   
			return _rootlessPath[(int)location];
		}
		
		/// <summary>
		/// Case sensitive Pathname of the FS Object without its rootless location's path.  
		/// </summary>
		/// <param name="location">
		/// A <see cref="MNFSObjectLocation"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.String"/>
		/// </returns>
		public string GetRootlessPathnameForLocation(MNFSObjectLocation location) {
			return System.IO.Path.Combine(GetRootlessPathForLocation(location), GetNameForLocation(location));
		}
		*/
		
		public MNFSObject() {
			_name = string.Empty;
			//_names = new string[(int)MNFSObjectLocation.NumberOfLocations];
			_rootlessPath = string.Empty; //new string[(int)MNFSObjectLocation.NumberOfLocations];
		}
	
		#region serialization
		private const string kSerialiseName = "n";
		private const string kSerialiseRootlessPath = "rp";
		
		public MNFSObject(SerializationInfo info, StreamingContext ctxt) {					
			_name = info.GetString(kSerialiseName);
			_rootlessPath = info.GetString(kSerialiseRootlessPath);		
			//replace the generic directory separator
			_rootlessPath = _rootlessPath.Replace(CrossPlatformDirectorySeperatorPlaceHolder, Path.DirectorySeparatorChar.ToString());
		}
		
		public void GetObjectData(SerializationInfo info, StreamingContext ctxt) {
		    info.AddValue(kSerialiseName, _name);
			 //make sure saved paths will work on all platforms
			info.AddValue(kSerialiseRootlessPath, _rootlessPath.Replace(Path.DirectorySeparatorChar.ToString(), CrossPlatformDirectorySeperatorPlaceHolder));
		}
		
		#endregion
	}
	
	[Serializable]
    public class MNSynchronisedFSObject : MNFSObject, ISerializable
    {
		#region variables
		
		protected DateTime[] _dateModified;
        protected DateTime[] _dateFound;		
        protected bool[] _existsAt = null;
        protected bool[] _synchroniseTo = null; //true if the files in directory A set to sync to the B.	
		protected bool _private;
		protected bool _ignore; //user can chose which directories not to scan.
		
		#endregion
		
		# region getters and setters

		public DateTime[] DateFound {
			get {
				return this._dateFound;
			}
			set {
				_dateFound = value;
			}
		}

		public DateTime[] DateModified {
			get {
				return this._dateModified;
			}
			set {
				_dateModified = value;
			}
		}

		public Boolean[] ExistsAt {
			get {
				return this._existsAt;
			}
			set {
				_existsAt = value;
			}
		}

		public Boolean[] SynchroniseTo {
			get {
				return this._synchroniseTo;
			}
			set {
				_synchroniseTo = value;
			}
		}
		
		public bool Private {
			get {
				return this._private;
			}
			set {
				//don't turn something private if it already exists at the source.
				if(!this.GetExistsAtForLocation(MNFSObjectLocation.Source)) {
					_private = value;
				} else {
					_private = false;
				}
			}
		}
		
		public bool Ignore {
			get {
				return this._ignore;
			}
			set {
				_ignore = value;
			}
		}			
		
		#endregion
		
		#region location helpers
		
		public DateTime GetDateModifiedForLocation(MNFSObjectLocation location)
        {   return _dateModified[(int)location]; }
		
		public string GetDateModifiedAsStringForLocation(MNFSObjectLocation location)
		{
			//TODO: return a string like the finder osx, today, yesterday, monday etc....
			return _dateModified[(int)location].ToString(); 
		}
		
		public DateTime GetDateFoundForLocation(MNFSObjectLocation location) 
        {   return _dateFound[(int)location]; }
		
        public Boolean GetExistsAtForLocation(MNFSObjectLocation location)
        {   return _existsAt[(int)location]; }
		
		public bool GetExistsAtAnyLocation() {
			bool result = false;
			for (int i = 0; i <= _existsAt.GetUpperBound(0) ; i++) {
				result = result || _existsAt[i];
			}	
			return result;
		}
		
        public Boolean GetSynchroniseToForLocation(MNFSObjectLocation location)
        {   return _synchroniseTo[(int)location]; }
		
        public void SetSynchroniseToForLocation(MNFSObjectLocation location, bool value) {   
			_synchroniseTo[(int)location] = value; 
			/*if(value) {
				//turn off syncing in opposite direction
				_synchroniseTo[(int)MNLocation.GetOppositeLocation(location)] = false;
			}*/
		}
		
	
		public void SetExistsAtForLocation(MNFSObjectLocation location, bool value)
		{	_existsAt[(int)location] = value; }
		
		public void ToggleSynchroniseToForLocation(MNFSObjectLocation location) {
			SetSynchroniseToForLocation(location, !GetSynchroniseToForLocation(location));
		}
		
		#endregion
		
		#region constructor
		public MNSynchronisedFSObject() : base()	{
			_dateModified = new DateTime[(int)MNFSObjectLocation.NumberOfLocations];
			_dateFound = new DateTime[(int)MNFSObjectLocation.NumberOfLocations];
			_existsAt = new Boolean[(int)MNFSObjectLocation.NumberOfLocations];
			_synchroniseTo = new Boolean[(int)MNFSObjectLocation.NumberOfLocations];
			_private = false;
			_ignore = false;
		}
		
		#endregion

		#region serialization
		private const string kSerialisePrivate = "p";
		private const string kSerialiseDateModified = "dm";
		private const string kSerialiseDateFound = "df";
		private const string kSerialiseExistsAt = "ea";
		private const string kSerialiseSyncTo = "st";
		private const string kSerialiseIgnore = "i";
		
		public MNSynchronisedFSObject(SerializationInfo info, StreamingContext ctxt) : base (info, ctxt) {
			_private = (bool)info.GetBoolean(kSerialisePrivate);
            _dateModified = (DateTime[])info.GetValue(kSerialiseDateModified, typeof(DateTime[]));
			_dateFound  = (DateTime[])info.GetValue(kSerialiseDateFound,typeof(DateTime[]));
			_existsAt = (bool[])info.GetValue(kSerialiseExistsAt,typeof(bool[]));
			_synchroniseTo = (bool[])info.GetValue(kSerialiseSyncTo,typeof(bool[]));
			_ignore = (bool)info.GetValue(kSerialiseIgnore,typeof(bool));				
		}
		
		public new void GetObjectData(SerializationInfo info, StreamingContext ctxt) {
			base.GetObjectData(info, ctxt);	
			info.AddValue(kSerialisePrivate, _private);
			info.AddValue(kSerialiseDateModified, _dateModified);
			info.AddValue(kSerialiseDateFound, _dateFound);
			info.AddValue(kSerialiseExistsAt, _existsAt);
			info.AddValue(kSerialiseSyncTo, _synchroniseTo);
			info.AddValue(kSerialiseIgnore, _ignore);
		}		
		
		#endregion
		
		/// <summary>
		/// Controls whether of not the file should be visible for the given location. 
		/// </summary>
		/// <param name="location">
		/// A <see cref="MNFSObjectLocation"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.Boolean"/>, true if the fsobject should be shown.
		/// </returns>
		public bool Show(MNFSObjectLocation location) {
			if(_private) {
				if(location == MNFSObjectLocation.Source) {
					return false;
				} else {
					return true;
				}				
			} else {
				return true;
			}
		}		
    }
}
